﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Numerics;
using System.IO;

namespace BitcoinLibrary
{
    /**
     * A PeerAddress holds an IP address and port number representing the network location of
     * a peer in the BitCoin P2P network. It exists primarily for serialization purposes.
     */
    public class PeerAddress : Message
    {
        private static readonly long serialVersionUID = 7501293709324197411L;

        IPAddress addr;
        int port;
        BigInteger services;
        long time;

        public PeerAddress(NetworkParameters param, byte[] payload, int offset, int protocolVersion)
            : base(param, payload, offset, protocolVersion)
        {
        }

        public PeerAddress(IPAddress addr, int port, int protocolVersion)
        {
            this.addr = addr;
            this.port = port;
            this.protocolVersion = protocolVersion;
        }

        public void bitcoinSerializeToStream(Stream stream)
        {
            if (protocolVersion >= 31402)
            {
                int secs = (int)DateTime.Now.Second;    // TODO: OK?
                Utils.uint32ToByteStreamLE(secs, stream);
            }
            Utils.uint64ToByteStreamLE(BigInteger.Zero, stream);  // nServices.
            // Java does not provide any utility to map an IPv4 address into IPv6 space, so we have to do it by hand.
            byte[] ipBytes = addr.GetAddressBytes();    // TODO: correct?
            if (ipBytes.Length == 4)
            {
                byte[] v6addr = new byte[16];
                Array.Copy(ipBytes, 0, v6addr, 12, 4);
                v6addr[10] = (byte)0xFF;
                v6addr[11] = (byte)0xFF;
                ipBytes = v6addr;
            }
            stream.Write(ipBytes, 0, ipBytes.Length);
            // And write out the port.
            stream.Write(new byte[] { (byte)(0xFF & port) }, 0, 1);
            stream.Write(new byte[] { (byte)(0xFF & (port >> 8)) }, 0, 1);
        }

        public override void parse()
        {
            // Format of a serialized address:
            //   uint32 timestamp
            //   uint64 services   (flags determining what the node can do)
            //   16 bytes ip address
            //   2 bytes port num
            if (protocolVersion > 31402)
                time = readUint32();
            else
                time = -1;
            services = readUint64();
            byte[] addrBytes = readBytes(16);
            try
            {
                addr = new IPAddress(addrBytes);    // TODO: ok?
            }
            catch (Exception e)
            {
                throw e;  // Cannot happen.
            }
            port = ((0xFF & bytes[cursor++]) << 8) | (0xFF & bytes[cursor++]);
        }

        public override string ToString()
        {
            return "[" + addr.Address.ToString() + "]:" + port;
        }
    }
}
